package com.link.base.base.contract.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.link.base.base.approval.model.Flow;
import com.link.base.base.approval.service.FlowService;
import com.link.base.base.common.model.Attachment;
import com.link.base.base.common.service.AttachmentService;
import com.link.base.base.contract.dao.mybatis.mapper.ContractVerMapper;
import com.link.base.base.contract.model.ContractItem;
import com.link.base.base.contract.model.ContractVer;
import com.link.base.base.contract.model.ModCostChange;
import com.link.base.base.product.model.PartHistoryPrice;
import com.link.base.base.product.service.PartHistoryPriceService;
import com.link.base.base.project.model.Project;
import com.link.base.base.project.service.ProjectService;
import com.link.base.core.basic.util.BigDecimalUtils;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.ServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.util.RedisUtil;
import com.link.core.util.StringUtils;
import com.link.core.util.redisclient.LinkRedisClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 合同
 *
 * @author 阳晓琳
 * @date 2020-09-16 17:25:08
 */
@Service
public class ContractVerServiceImpl extends BasicServiceImpl<ContractVer> implements ContractVerService {

    @Resource
    private ContractVerMapper contractVerMapper;

    @Resource
    private FlowService flowService;

    @Resource
    private ContractItemService contractItemService;

    @Resource
    private ModCostChangeService modCostChangeService;

    @Resource
    private AttachmentService attachmentService;

    @Resource
    private ProjectService projectService;

    @Resource
    private PartHistoryPriceService partHistoryPriceService;


    @Override
    public BasicMapper<ContractVer> getBasicMapper() {
        return contractVerMapper;
    }

    /**
     * 复制按钮，点击确认后复制该合同所有信息，包括合同主体信息、价格信息
     * 将新合同状态改为“新建”；
     * 原合同状态改为“已失效”；
     * @param entity
     * @throws Exception
     * @return
     */
    @Override
    public ContractVer copyPointContract(ContractVer entity) throws Exception {
        //1.根据传过来的entityID查询定点合同(前端只传过来旧合同ID)
        ContractVer contractVer = contractVerMapper.queryById(entity);
        //2.新建合同对象，将定点合同的所有字段deepClone进来
        ContractVer contract1 = (ContractVer) contractVer.deepClone();
        contract1.setId(keyGenerateService.keyGenerate());
        //3.新合同对象状态设为新建insert，旧合同状态设为已失效update
        contract1.setContractStatus("New");
        contractVerMapper.insert(contract1);
        contractVer.setContractStatus("Invalid");
        contractVerMapper.update(contractVer);
        //4.新合同tab页产品信息子表复制，根据旧合同ID去产品表查询旧合同产品信息
        ContractItem con = new ContractItem();
        con.setContractId(contractVer.getId());
        List<ContractItem> contractItems = contractItemService.queryAll(con);
        ContractItem contractItem;
        if (contractItems != null && !contractItems.isEmpty()) {
            //4.1.遍历旧合同产品信息，复制插入
            for (ContractItem contractItem1 : contractItems) {
                contractItem = (ContractItem) contractItem1.deepClone();
                //4.2.修改新插入数据头ID，修改新插入的产品记录合同ID为新合同ID
                contractItem.setId(keyGenerateService.keyGenerate());
                contractItem.setContractId(contract1.getId());
                contractItemService.insert(contractItem);
            }
        }
        //5.新合同tab子表产品价格更新追踪表,根据旧合同ID去产品模具费用变更追踪表查询记录
        ModCostChange modCost = new ModCostChange();
        modCost.setPointContractId(contractVer.getId());
        List<ModCostChange> modCostChanges = modCostChangeService.queryAll(modCost);
        ModCostChange modCostChange;
        if (modCostChanges != null && !modCostChanges.isEmpty()) {
            //遍历旧定点合同价格追踪记录，复制
            for (ModCostChange modCostChange1 : modCostChanges) {
                modCostChange = (ModCostChange) modCostChange1.deepClone();
                //修改新记录头，修改追踪记录原定点合同ID
                modCostChange.setId(keyGenerateService.keyGenerate());
                modCostChange.setPointContractId(contract1.getId());
                modCostChangeService.insert(modCostChange);
            }
        }
        return contract1;
    }


    /**
     * 框架定点合同失效
     * @param entity
     * @throws Exception
     */
    @Override
    public void unactiveContract(ContractVer entity) throws Exception {
        //点击失效按钮，将合同状态变为失效
        entity.setContractStatus("Invalid");
        contractVerMapper.update(entity);
    }


    /**
     * 框架定点合同审批
     * 框架合同审批前校验附件
     * 定点合同审批前校验附件，产品行
     * @param entity
     * @throws Exception
     */
    @Override
    public void frameContractSubmit(ContractVer entity) throws Exception {
        ContractVer contractVer = contractVerMapper.queryById(entity);
        //框架定点合同审批前校验是否有附件
        //根据合同id去附件表查询记录
        Attachment atta = new Attachment();
        atta.setHeadId(contractVer.getId());
        List<Attachment> attachments = attachmentService.queryAttachmentByHeadId(atta);
        if (attachments == null || attachments.isEmpty()) {
            throw new ServiceException("IYUCONTRACT-001");
        }

        String approvalType = "ContractSubmit";
        String approvalType2 = "FrameSubmit";
        ObjectMapper mapper = new ObjectMapper();
        //把Java对象转化成json字符串
        String approvalDetail = mapper.writeValueAsString(contractVer);
        if ( "SentinelCon".equals(contractVer.getContractType())) {
            //定点合同审批前校验
            ContractItem conItem = new ContractItem();
            conItem.setContractId(contractVer.getId());
            List<ContractItem> contractItems = contractItemService.queryAll(conItem);
            if (contractItems == null || contractItems.isEmpty()) {
                throw new ServiceException("IYUCONTRACT-002");
            }
            //创建定点合同审批流
            flowService.createFlow(contractVer.getId(),approvalType,null,contractVer.getContractName(),contractVer.getId(),approvalDetail);
        } else {
            //创建框架合同审批流
            flowService.createFlow(contractVer.getId(),approvalType2,null,contractVer.getContractName(),contractVer.getId(),approvalDetail);
        }
        contractVer.setContractStatus("Submitted");
        contractVerMapper.update(contractVer);
    }


    /**
     * 框架定点合同审批通过
     * @param flow
     * @param arg1
     * @param arg2
     * @param arg3
     * @param arg4
     * @param arg5
     * @throws Exception
     */
    @Override
    public void agreeApproval(Flow flow, String arg1, String arg2, String arg3, String arg4, String arg5) throws Exception {
        Long frameContractId = flow.getFlowObjId();
        ContractVer contractVer = new ContractVer();
        contractVer.setId(frameContractId);
        ContractVer contract1 = contractVerMapper.queryById(contractVer);
        contract1.setContractStatus(arg1);
        contractVerMapper.update(contract1);
    }

    /**
     * 框架定点合同编码字段只读，由系统自动生成，规则如下：
     * 客户编码+合同类型首字母（框架合同KJ，定点合同DD，模具合同MJ，价格合同JG）+年月日（YYYYMMDD）+流水号（001-999），适用于所有合同
     * @param entity
     * @throws Exception
     */
    @Override
    public void beforInsert(ContractVer entity) throws Exception {

        if (null == entity.getAcctId() && null == entity.getProjectId()) {
            throw new RuntimeException("签约客户和项目不能同时为空!");
        }
        if (null != entity.getAcctId() && null != entity.getProjectId()) {
            throw new RuntimeException("签约客户和项目不能同时存在!");
        }
        //定点合同保存按钮新增校验：保存时校验数据库中该定点合同选择的的项目id所关联的定点合同id的合同状态是否全部都是已失效的
        //为了保证定点合同产品行 根据项目ID和报价ID 唯一匹配零件历史价格
        if ("SentinelCon".equals(entity.getContractType())) {
            ContractVer querFixCon = new ContractVer();
            querFixCon.setProjectId(entity.getProjectId());
            //查询是否有该项目未失效的定点合同，如果有报错：该项目已存在未失效定点合同，请勿重复创建！
            List<ContractVer> contracts = contractVerMapper.queryByProjectId(querFixCon);
            if (!contracts.isEmpty()) {
                throw new ServiceException("IYUCONTRACT-014");
            }
        }


        //合同编码自动生成
        //1.获取客户编码 (根据项目id查询客户编码，项目关联了客户)
        String acctCode = entity.getAcctCode();
        if (StringUtils.isNull(acctCode)) {
            if (null != entity.getProjectId()) {
                Project projectMod = new Project();
                projectMod.setId(entity.getProjectId());
                Project project = projectService.queryById(projectMod);
                if (project == null) {
                    throw new ServiceException("IYUCONTRACT-005");
                }
                acctCode = project.getAcctCode();
                // 添加客户ID
                Long acctId = project.getAcctId();
                entity.setAcctId(acctId);
            }
        }
        if (StringUtils.isNull(acctCode)) {
            throw new RuntimeException("客户编码获取异常，请联系管理员!");
        }

        //2.判断合同类型获取字符
        String contractType;
        if ("FrameCon".equals(entity.getContractType())) {
            //框架合同字符编码
            contractType = "KJ";
        } else if ("SentinelCon".equals(entity.getContractType())) {
            //定点合同字符编码
            contractType = "DD";
        } else if ("ModelCon".equals(entity.getContractType())) {
            //模具合同字符编码
            contractType = "MJ";
        } else {
            //价格合同字符编码
            contractType = "JG";
        }
        //3.获取年月日字符串
        Date today = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
        //将日期格式化为日期/时间字符串
        String day = format.format(today);
        //4.流水号生成（每个客户每年刷新一回,换一次key，就会重新计数一次）
        Calendar thisYear = Calendar.getInstance();
        String year = String.valueOf(thisYear.get(Calendar.YEAR));
        LinkRedisClient jedis = RedisUtil.getJedis();
        String zeroCode;
        try {
            //判断redisKey是否存在
            String redisKey = "contract_code" + "/" + year;
            if (jedis.exists(redisKey)) {
                //判断哈希表的指定字段是否存在
                if (jedis.hexists(redisKey,acctCode + "/" + contractType)) {
                    //存在则流水号取出+1
                    String getCode = jedis.hget(redisKey,acctCode + "/"  + contractType);
                    int code = Integer.parseInt(getCode);
                    //四位流水号补零
                    zeroCode = String.format("%04d",code);
                    //使用后+1
                    jedis.hincrBy(redisKey,acctCode + "/"  + contractType,1);
                } else {
                    //不存在则说明当前为该客户今年第一个该类型的合同
                    zeroCode = String.format("%04d",1);
                    jedis.hset(redisKey,acctCode + "/"  + contractType, String.valueOf(2));
                }
            } else {
                //redisKey不存在去数据库查开头客户编号为acctCode的当年流水号排到哪
                List<ContractVer> contracts = contractVerMapper.queryByContractCode(acctCode,contractType);
                if (contracts.size() != 0) {
                    //存在则在最新客户合同记录编号尾数的基础上+1
                    String theAcctcode = contracts.get(0).getAcctCode();
                    //取最新合同记录的合同编号的后四位流水号并+1塞入zerocode
                    int theCode = Integer.parseInt(theAcctcode.substring(theAcctcode.length() - 4));
                    ++theCode;
                    zeroCode = String.format("%04d", theCode);
                    //重新为jedis塞入值再+1
                    jedis.hset(redisKey,acctCode + "/"  + contractType,zeroCode);
                    jedis.hincrBy(redisKey,acctCode + "/"  + contractType,1);
                } else {
                    //不存在则说明当前为该客户今年第一个合同（不论什么类型的合同都没有）
                    zeroCode = String.format("%04d",1);
                    jedis.hset(redisKey,acctCode + "/"  + contractType, String.valueOf(2));
                }
            }
            entity.setContractCode(acctCode + contractType + day + zeroCode);
        } catch (Exception e) {
            LCLogger.withException(e);
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    /**
     * 每月查询一次
     * 查询查询近一个月内生效的定点合同对应产品行，为对应产品更新零件历史价格信息
     * @throws Exception
     */
    @Override
    public void upsertPartHistory() throws Exception {
        //查询最后更新时间是上个月的生效的定点合同的产品行
        ContractItem querItem = new ContractItem();
        querItem.setAttr1("querylastyear");
        List<ContractItem> contractItems = contractItemService.queryAll(querItem);
        //查询创建时间两年内的零件历史价格记录，用产品行匹配历史价格，匹配成功更新零件历史价格
        PartHistoryPrice querPart = new PartHistoryPrice();
        querPart.setAttr1("query2Year");
        List<PartHistoryPrice> partHistoryPrices = partHistoryPriceService.queryAll(querPart);

        for (ContractItem contractItem : contractItems) {
            for (PartHistoryPrice partHistoryPrice : partHistoryPrices) {
                if (contractItem.getProjectId().equals(partHistoryPrice.getProjectHeadId())
                        && contractItem.getProdQuoteId().equals(partHistoryPrice.getProdQuoteId())) {
                    //项目ID与最新报价ID唯一匹配一条零件历史价格记录，更新本条记录的定点价、定点模具费、定点毛利率、PCD毛利率
                    BigDecimal noTaxPrice = BigDecimalUtils.changeNullToZero(contractItem.getNoTaxPrice());
                    BigDecimal factoryCost = BigDecimalUtils.changeNullToZero(partHistoryPrice.getFactoryCost());
                    BigDecimal pCDPartCost = BigDecimalUtils.changeNullToZero(partHistoryPrice.getpCDPartCost());
                    //定点价：匹配一致后，取该定点合同产品行的未税单价。
                    partHistoryPrice.setFixedPrice(noTaxPrice);
                    //定点模具费：每月查询一次。查询近一个月内生效的定点合同，将该零件的定点模具费呈现至历史价格列表中。匹配一致后，取该定点合同产品行id的正式模具费。
                    partHistoryPrice.setFixedMouldCost(contractItem.getOffcialFee());
                    //定点毛利率 =（定点价-工厂成本（未含模摊））/定点价。其中：定点价为序号m取得的定点价。工厂成本（未含模摊）来源为最终伦次总成报价id关联的成本id的工厂成本（未含模摊）的值。
                    //PCD毛利率：计算公式为（定点价- PCD零件实际成本）/定点价。其中：定点价为序号m取得的定点价。PCD零件实际成本为手工输入的值。
                    if (noTaxPrice.equals(BigDecimal.ZERO)) {
                        partHistoryPrice.setFixedGM(BigDecimal.ZERO);
                        partHistoryPrice.setpCDGrossMargin(BigDecimal.ZERO);
                    } else {
                        partHistoryPrice.setFixedGM(noTaxPrice.subtract(factoryCost).divide(noTaxPrice,7,BigDecimal.ROUND_HALF_UP));
                        partHistoryPrice.setpCDGrossMargin(noTaxPrice.subtract(pCDPartCost).divide(noTaxPrice,7,BigDecimal.ROUND_HALF_UP));
                    }
                    partHistoryPriceService.update(partHistoryPrice);
                    break;
                }
            }
        }
    }
}
